#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kfifo.h>

#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/regs-gpio.h>
#include <asm/io.h>

#include "xiaomi_led.h"

#define VSER_MAJOR	444
#define VSER_MINOR	0
#define VSER_DEV_CNT	1
#define VSER_DEV_NAME	"xiaomi_led"

struct vser_dev {
	unsigned int baud;
	struct option opt;
	struct cdev cdev;
	bool led;
};

DEFINE_KFIFO(vsfifo, char, 32);
static struct vser_dev vsdev;

static int vser_open(struct inode *inode, struct file *filp)
{
	printk(KERN_INFO"open!!\n");
	return 0;
}

static int vser_release(struct inode *inode, struct file *filp)
{
	return 0;
}

static ssize_t vser_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
	int ret;
	unsigned int copied = 0;

	ret = kfifo_to_user(&vsfifo, buf, count, &copied);
	printk(KERN_INFO"read!!!\n");
	return ret == 0 ? copied : ret;
}

static ssize_t vser_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
{
	int ret;
	unsigned int copied = 0;

	ret = kfifo_from_user(&vsfifo, buf, count, &copied);
	
	printk(KERN_INFO"write!!\n");
	return ret == 0 ? copied : ret;
}

static long vser_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	if (_IOC_TYPE(cmd) != VS_MAGIC)
		return -ENOTTY;
	printk(KERN_INFO"ioctl!!%d \n",cmd);	

	switch (cmd) {
	case VS_SET_LED_ON:
		gpio_set_value( EXYNOS4_GPX2(7) , 1 );
		gpio_set_value( EXYNOS4_GPX1(0) , 1 );
		gpio_set_value( EXYNOS4_GPF3(4) , 1 );
		gpio_set_value( EXYNOS4_GPF3(5) , 1 );	
		printk(KERN_INFO" VS_SET_LED_ON \n");
		break;	
	case VS_SET_LED_OFF:
		gpio_set_value( EXYNOS4_GPX2(7) , 0 );
		gpio_set_value( EXYNOS4_GPX1(0) , 0 );
		gpio_set_value( EXYNOS4_GPF3(4) , 0 );
		gpio_set_value( EXYNOS4_GPF3(5) , 0 );
		printk(KERN_INFO" VS_SET_LED_OFF \n");
		break;		
	case VS_SET_BAUD:
		vsdev.baud = arg;
		break;
	case VS_GET_BAUD:
		arg = vsdev.baud;
		break;
	case VS_SET_FFMT:
		if (copy_from_user(&vsdev.opt, (struct option __user *)arg, sizeof(struct option)))
			return -EFAULT;
		break;
	case VS_GET_FFMT:
		if (copy_to_user((struct option __user *)arg, &vsdev.opt, sizeof(struct option)))
			return -EFAULT;
		break;
	default:
		return -ENOTTY;
	}

	return 0;
}

static struct file_operations vser_ops = {
	.owner = THIS_MODULE,
	.open = vser_open,
	.release = vser_release,
	.read = vser_read,
	.write = vser_write,
	.unlocked_ioctl = vser_ioctl,
};

static int __init vser_init(void)
{
	int ret;
	dev_t dev;

	dev = MKDEV(VSER_MAJOR, VSER_MINOR);
	ret = register_chrdev_region(dev, VSER_DEV_CNT, VSER_DEV_NAME);
	if (ret)
		goto reg_err;

	cdev_init(&vsdev.cdev, &vser_ops);
	vsdev.cdev.owner = THIS_MODULE;
	vsdev.baud = 115200;
	vsdev.opt.datab = 8;
	vsdev.opt.parity = 0;
	vsdev.opt.stopb = 1;

	ret = cdev_add(&vsdev.cdev, dev, VSER_DEV_CNT);
	if (ret)
		goto add_err;

	gpio_request( EXYNOS4_GPX2(7), VSER_DEV_NAME );
	if (ret < 0)
		printk(KERN_INFO"gpio_request EXYNOS4_GPX2(7) fail! \n");
	gpio_request( EXYNOS4_GPX1(0), VSER_DEV_NAME );
	if (ret < 0)
		printk(KERN_INFO"gpio_request EXYNOS4_GPX1(0) fail! \n");	
	gpio_request( EXYNOS4_GPF3(4), VSER_DEV_NAME );
	if (ret < 0)
		printk(KERN_INFO"gpio_request EXYNOS4_GPF3(4) fail! \n");
	ret = gpio_request( EXYNOS4_GPF3(5), VSER_DEV_NAME );
	if (ret < 0)
		printk(KERN_INFO"gpio_request EXYNOS4_GPF3(5) fail! \n");

	s3c_gpio_cfgpin( EXYNOS4_GPX2(7), S3C_GPIO_OUTPUT );
	s3c_gpio_cfgpin( EXYNOS4_GPX1(0), S3C_GPIO_OUTPUT );
	s3c_gpio_cfgpin( EXYNOS4_GPF3(4), S3C_GPIO_OUTPUT );
	s3c_gpio_cfgpin( EXYNOS4_GPF3(5), S3C_GPIO_OUTPUT );

	gpio_set_value( EXYNOS4_GPX2(7) , 1 );
	gpio_set_value( EXYNOS4_GPX1(0) , 1 );
	gpio_set_value( EXYNOS4_GPF3(4) , 1 );
	gpio_set_value( EXYNOS4_GPF3(5) , 1 );	
	printk(KERN_INFO"vser_init sucess! \n");
	return 0;

add_err:
	unregister_chrdev_region(dev, VSER_DEV_CNT);
reg_err:
	return ret;
}

static void __exit vser_exit(void)
{
	
	dev_t dev;

	dev = MKDEV(VSER_MAJOR, VSER_MINOR);

	cdev_del(&vsdev.cdev);
	unregister_chrdev_region(dev, VSER_DEV_CNT);
}

module_init(vser_init);
module_exit(vser_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kevin Jiang <jiangxg@farsight.com.cn>");
MODULE_DESCRIPTION("A simple character device driver");
MODULE_ALIAS("virtual-serial");
